#pragma once
#include "nocopy.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"
#include <functional>
#include <memory>
#include <unordered_map>
using std::function;
using std::weak_ptr;
using std::shared_ptr;
using std::unordered_map;

class Connection;
class TcpServer;

//using func_t = function<void(Connection)>;
using func_t = function<void(weak_ptr<Connection>)>;
//using func_t = function<void(shared_ptr<Connection>)>;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

int debug_cnt = 0;
const int maxnum = 64;
const int buffer_size = 1024;

class Connection
{
public:
    Connection(){};
    Connection(int sock)
    :_sock(sock)
    {}
    ~Connection()
    {}

    //每个链接自己可以通过这个接口在不同时间注册不同的回调，若只在构造初始化则缺失了这种灵活性
    void SetHandler(func_t rcv_cb, func_t snd_cb, func_t ecp_cb)
    {
        _rcv_cb = rcv_cb; 
        _snd_cb = snd_cb;   
        _ecp_cb = ecp_cb;     
    }

    int GetSock() {return _sock;}

    void AppendInbuffer(const string& in) {_inbuffer += in;}
    void AppendOutbuffer(const string& in) {_outbuffer += in;}
    string& GetInbuffer() {return _inbuffer;}
    string& GetOutbuffer() {return _outbuffer;}

private:
    int _sock;
    //为每个链接都配置读写缓冲区    理解时注意，这里并不是内核的接发缓冲区！
    string _inbuffer;   //这里先用string做缓冲区，实际该用vector（因为string处理不了二进制数据）
    string _outbuffer;
public:
    //为每个链接都注册回调
    func_t _rcv_cb;     //读事件
    func_t _snd_cb;     //写事件
    func_t _ecp_cb;     //异常事件

    weak_ptr<TcpServer> _tcpsvr_ptr;
};


class TcpServer : public nocopy
{
public:

    //新增一个链接，当然需要用参数对链接的成员字段进行初始化(主要是对其注册自己的回调方法)
    void AddConnection(int sock, uint32_t events, func_t rcv_cb, func_t snd_cb, func_t ecp_cb)
    {
        shared_ptr<Connection> new_connection(new Connection(sock));
        new_connection->SetHandler(rcv_cb, snd_cb, ecp_cb);

        _connections[sock] = new_connection;

        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, events);
        
    }

    bool IsConnectionSafe(int fd)
    {
        if(_connections.count(fd) == 0)
        {
            return false;
        }
        return true;
    }

    void Accepter(weak_ptr<Connection> conn)    //bind函数的第二个参数和被绑定函数的参数类型无关
    {
        //1. 正常accept获取newsock，建立连接
        string clientip;
        uint16_t clientport;
        int newsock = _listensock_ptr->Accpet(&clientip, &clientport);
        if(newsock > 0)
        {
            log(Debug, "[%s]:[%d] linked! ", clientip.c_str(), clientport);

        }

        //2. 为获取到的newsock创建它自己的Connection   本质是是读事件
        AddConnection(newsock, EVENT_IN,
        std::bind(&TcpServer::Receiver, this, std::placeholders::_1)
        ,std::bind(&TcpServer::Sender, this, std::placeholders::_1)
        ,std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
    }

    void Init()
    {
        _listensock_ptr->Socket();
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        //在初始化服务器时可直接为listensock建立链接
        int fd = _listensock_ptr->GetFd();
        AddConnection(fd, EVENT_IN, std::bind(&TcpServer::Accepter,this,std::placeholders::_1), nullptr, nullptr);  //注意bind要指明类域
        _quit = false;
        
    }

    void Receiver(weak_ptr<Connection> conn)
    {
        if(conn.expired())   return;
        
        auto c = conn.lock();
        int fd = c->GetSock();
        while(1)
        {
            char r_buffer[buffer_size] = {0};
            int n = recv(fd, r_buffer, sizeof(r_buffer)-1, 0);
            if(n > 0)   //正常读
            {
                c->AppendInbuffer(r_buffer);
            }
            else if(n == 0)     //链接关闭 --> 不用在这“自己”处理，而是交给自己的异常处理回调！
            {
                log(Info,"client quit...");
                c->_ecp_cb(conn);
                return;     //链接关闭直接返回，不需要也不能让给上层_on_manage回调了！
            }
            else    //非阻塞读完或读出错
            {   
                if(errno == EWOULDBLOCK)
                {
                    log(Info,"recv already done...");
                    break;
                }
                else if(errno == EINTR)     //信号影响
                {
                    continue;
                }
                else
                {
                    log(Error,"recv falied.");
                    c->_ecp_cb(c);      //读出错，转入自己的异常处理，处理后直接返回
                    return;
                }
            }         
            
        }       
        //DebugPrint();
        _on_manage(conn);
    }

    void EventEnable(int sock, bool readable, bool writeable)   //注意还要保持ET模式
    {
        if(readable) _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, EPOLLIN | EPOLLET);
        if(writeable) _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, EPOLLOUT| EPOLLET);
    }

    void Sender(weak_ptr<Connection> conn)
    {
        if(conn.expired())   return;

        //发一次，若发送缓冲区中还有数据，就常关心
        auto c = conn.lock();
        int fd = c->GetSock();
        while (1)
        {
            auto& w_buffer = c->GetOutbuffer();     //细节：auto只识别引用前的类型而不会识别引用
            int n = send(fd, w_buffer.c_str(), w_buffer.size(), 0);
            if(n > 0)
            {
                w_buffer.erase(0,n);
                if(w_buffer.empty())    
                    break;
            }
            else if(n == 0)     //可能某些原因，缓冲区中无数据，所以什么都没发（罕见）
            {
                continue;
            }
            else        
            {
                if(errno == EWOULDBLOCK)    //可能发完了，也可能是对方的接收缓冲区不够了（没错，对方接收缓冲区不足时send会返回-1！）
                {
                    if(!w_buffer.empty())
                        EventEnable(fd,true,true);
                    else
                        EventEnable(fd,true,false);     //关闭对写事件的关心
                    break;
                }                
                else if(errno == EINTR)     //信号影响
                {
                    continue;
                }
                else
                {
                    c->_ecp_cb(conn);
                }
            }
        }
        
    }

    void Excepter(weak_ptr<Connection> conn)
    {
        log(Info,"Dealing with the exception.");
        //1. 从epoll中移除对异常文件描述符的关系
        auto c = conn.lock();
        int fd = c->GetSock();
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);

        //2. 关闭异常的文件描述符
        close(fd);

        //3. 从_connections中删除
        _connections.erase(fd);
    }

    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollerWait(_ep_events,maxnum);
        log(Info,"this is %d times to dispatch", debug_cnt++);

        for(int i = 0; i < n; i++)
        {
            int fd = _ep_events[i].data.fd;

            //先为该链接设置非阻塞
            SetNoneBlock(fd);

            if(IsConnectionSafe(fd))
            {
                //由于在Init时已为_listensock进行事件与回调的注册，故这里不用再特殊判断！
                //根据读/写事件直接调用链接自己的回调即可！
                //细节：并不是非读即写！

                // 统一把事件异常转换成为读写问题
                if (_ep_events[i].events & EPOLLERR)
                    _ep_events[i].events |= (EPOLLIN | EPOLLOUT);

                if (_ep_events[i].events & EPOLLHUP)
                    _ep_events[i].events |= (EPOLLIN | EPOLLOUT);

                if(_ep_events[i].events & EVENT_IN) 
                {   
                    if(_connections[fd]->_rcv_cb)   //要先判断是否注册了
                    {
                        _connections[fd]->_rcv_cb(_connections[fd]);
                    }
                }
                //DebugPrint();

                if(_ep_events[i].events & EVENT_OUT) 
                {
                    if(_connections[fd]->_snd_cb)
                    {
                        _connections[fd]->_snd_cb(_connections[fd]);
                    }
                }
                
            }
        }
    }

    void DebugPrint()
    {
        cout << "now we have fds: ";
        for(auto& e : _connections)
        {
            cout << e.first << " ";
        }
        cout << endl;
        cout << "inbuffer contents: " << endl;
        for(auto& e : _connections)
        {
            cout << e.first << ":" << endl;
            cout << e.second->GetInbuffer() << endl;
        }
    }

    void Loop()
    {
        while (!_quit)
        {
            Dispatcher(-1);
            DebugPrint();
        }
        
    }

    TcpServer(uint16_t port, func_t onmanage)
    :_port(port), _epoller_ptr(new Epoller()), _listensock_ptr(new Sock())  //注意要先给指针开空间！！！
    ,_on_manage(onmanage)
    {}
    ~TcpServer()
    {}


private:   
    epoll_event _ep_events[maxnum];      //所“捞取”的多个fd即对应事件集的“存放点”
    shared_ptr<Epoller> _epoller_ptr;      //和内核交互
    unordered_map<int,shared_ptr<Connection>> _connections;

    shared_ptr<Sock> _listensock_ptr;
    uint16_t _port;

    func_t _on_manage;

    bool _quit;
};

